# -*- coding: utf-8 -*-


# cmap2owl -- Helper application to convert from concept maps to OWL ontologies
# Copyright (c) 2008-2013  Rodrigo Rizzi Starr
#  
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#  
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#  
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


'''
Searches the CMap concepts for phrases that are a disjunction of concepts, that
is, phrases of the form 'Concept1, concept 2 or concept 3'. When these are
found, the user is asked if s/he wants to split it in the composing concepts.

@author: Rodrigo Rizzi Starr
@copyright: Copyright © 2008-2013 Rodrigo Rizzi Starr
@license: MIT License
@contact: rodrigo.starr@gmail.com
'''


from CMap2Prolog import assemblyQuery
from infoProvider import TextGenInfoProvider
from mapperRule import MapperRule
from mapperQuestion import MapperQuestion
from linguisticRules import DisjunctionClasses, ConjunctionClasses
from log import log
log.debug('Loaded...')


class SplitDisjunctionRule(MapperRule):
    'Infers the existence of several classes mentioned on the same cm concept'

    def __init__(self, db):

        # Rules to create questions from the text identification phase
        rules = [
                 'inferenceGroup(disjunctionOfConcepts, '
                                '[disjunctionOfConcepts(_, _, _)])',

                 'class(DerivedId) :- '
                    'isTrue(disjunctionOfConcepts(_, ListOfClasses, _)), '
                    'member(DerivedId, ListOfClasses)',

                 'isTrue(class(DerivedId)) :- '
                    'isTrue(disjunctionOfConcepts(_, ListOfClasses, _)), '
                    'member(DerivedId, ListOfClasses)',

#                 'relationBetween(LP, From, To) :- '
#                    'isTrue(disjunctionOfConcepts(IdTo, ListOfClasses, _)), '
#                    'equivalentConcept(IdTo, EqualTo), '
#                    'relationCM(LP, EqualFrom, EqualTo), '
#                    'equivalentLinkingPhrase(LP, EqualLinkingPhrase), '
#                    'equivalentConcept(From, EqualFrom), '
#                    'member(To, ListOfClasses)',
# 
#                 'relationBetween(LP, From, To) :- '
#                    'isTrue(disjunctionOfConcepts(IdFrom, ListOfClasses, _)), '
#                    'equivalentConcept(IdFrom, EqualFrom), '
#                    'relationCM(LP, EqualFrom, EqualTo), '
#                    'equivalentLinkingPhrase(LP, EqualLinkingPhrase), '
#                    'equivalentConcept(To, EqualTo), '
#                    'member(From, ListOfClasses)',

                 'isFalse(class(Id)) :- isTrue(disjunctionOfConcepts(Id, _, _))',
                    
                 'classOWL(DerivedId) :-'
                    'isTrue(disjunctionOfConcepts(_, ListOfClasses, _)), '
                    'member(DerivedId, ListOfClasses)',
                   ]
          
        MapperRule.__init__(self, 'SplitDisjunction', db, rules)
      
    def createRule(self, phrases=[]):
        '''For each phrase in the cmap, if it triggers the DisjunctionClasses
           rule. If it does, then assert the needed facts.
        '''

        # We have to tell prolog that we will use a dynamic predicate
        self.db.dynamic('disjunctionOfConcepts/3')
        
        # Now we apply the linguistic rule for each phrase
        dc = DisjunctionClasses()
        cc = ConjunctionClasses()

        results = []
        for phrase in phrases:
            results.extend(dc.apply(phrase))
            results.extend(cc.apply(phrase))

        # We keep track of asserted facts because a node is part of several
        # phrases (this phrase structure is useful but not very
        # efficient). Also, sometimes both rules may give a true positive.
        assertedFacts = set()
        for result in results:
            # The predicate form is:
            # disjunctionOfConcepts(ConceptCMId, ListOfClasses, ListOfLabels)
            # (It follows the same structure of LogicalConnectiveResult)
            fact = ('disjunctionOfConcepts(%(id)s, %(listOfClasses)s, '
                                          '%(listOfClassLabels)s)')
            data = {'id': result.nodeId,
                    'listOfClasses': result.listOfClasses,
                    'listOfClassLabels': result.listOfClassLabels,
                    }
            fact = assemblyQuery(fact, data)
            
            if fact not in assertedFacts:
                assertedFacts.add(fact)
                self.db.assertz(fact)

            labelPattern = 'label(%(class)s, %(classLabel)s)'
            llabelPattern = 'llabel(%(class)s, %(classLabelLow)s)'
            for klass, label in zip(result.listOfClasses, result.listOfClassLabels):
                data = {'class': klass,
                        'classLabel': label,
                        'classLabelLow': label.lower()}
                label = assemblyQuery(labelPattern, data)
                llabel = assemblyQuery(llabelPattern, data)
            
                if label not in assertedFacts:
                    assertedFacts.add(label)
                    self.db.assertz(label)
                if llabel not in assertedFacts:
                    assertedFacts.add(llabel)
                    self.db.assertz(llabel)
                
                
        # This will assert all prolog rules. Must be done before we process the
        # phrases
        MapperRule.createRule(self, phrases)
            
    def getQuestions(self):
        '''Returns a list of questions if a certain concept is a disjunction of
           classes.
        '''
        
        questions = []
        query = ('possible(disjunctionOfConcepts(Concept, ListOfClasses, '
                                                'ListOfLabels))')
        
        classes = list(self.db.query(query))
        
        for answer in classes:
            answer['Ids'] = [answer['Concept']]
            questions.append(MapperQuestion(
                self.db,
                answer,
                'The label "<b>%(ListOfLabels)s</b>" looks like several '
                'concepts together. Should I split them?',
                'disjunctionOfConcepts(%(Concept)s, %(ListOfClasses)s, '
                                      '%(ListOfLabels)s)',
                 helpFile = 'disjunctionOfConcepts',
            ))
            
        return questions
      
    def getInfoProvider(self):
        'Returns an info provider about classes and individuals'
        
        infoProvider = TextGenInfoProvider(self.db,
                                'isTrue(disjunctionOfConcepts(%s, '
                                       'ListOfClasses, '
                                       'ListOfLabels))',
                                'This node was split in several separated '
                                           'concepts: %(ListOfLabels)s.')
        
        return infoProvider
        
        
    
